﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Web.UI;
using System.Web.UI.WebControls;

namespace Pickle.Core.ExtensionMethods
{
	/// <summary>
	/// Where should replicated input
	/// </summary>
	public enum ReplicationMode
	{
		/// <summary>
		/// Replicated input is added behind the source
		/// </summary>
		Append,
		/// <summary>
		/// Replicated input is added in front of the source
		/// </summary>
		Prepend
	}

	/// <summary>
	/// Extension methods
	/// </summary>
	public static class Extensions
	{
		/// <summary>
		/// Returns the nth Fibonacci number from the Fibonacci sequence using recursion
		/// </summary>
		/// <param name="n"></param>
		/// <returns></returns>
		public static int ToFibonacci(this int n)
		{
			return Utilities.Fibonacci(n);
		}

		/// <summary>
		/// Replicates the input string the specified number of times in front of or behind the source string
		/// </summary>
		/// <param name="source">The string that the replicated input is being added to.</param>
		/// <param name="input">The string to replicate.</param>
		/// <param name="count">The number of times to replicate the input.</param>
		/// <param name="replicationMode">The positioning of the replicated data.</param>
		/// <returns>The source string if the input is null, empty or the count is less than one.  Otherwise the processed result of replication.</returns>
		public static string Replicate(this string source, string input, int count, ReplicationMode replicationMode)
		{
			// validate input
			if (string.IsNullOrEmpty(input) == true) return source;
			if (count < 1) return source;

			// Prime the result
			StringBuilder Result = new StringBuilder(source.Length + (input.Length * count));
			Result.Append(source);

			for (int i = 0; i < count; i++)
			{
				if (replicationMode == ReplicationMode.Append)
					Result.Append(input);
				else
					Result.Insert(0, input);
			}

			return Result.ToString();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="controls"></param>
		/// <returns></returns>
		public static IEnumerable<MenuItem> All(this MenuItemCollection controls)
		{
			foreach (MenuItem control in controls)
			{
				foreach (MenuItem grandChild in control.ChildItems.All())
					yield return grandChild;

				yield return control;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="controls"></param>
		/// <returns></returns>
		/// <remarks>http://weblogs.asp.net/dfindley/archive/2007/06/29/linq-the-uber-findcontrol.aspx</remarks>
		public static IEnumerable<Control> All(this ControlCollection controls)
		{
			foreach (Control control in controls)
			{
				foreach (Control grandChild in control.Controls.All())
					yield return grandChild;

				yield return control;
			}
		}

		/// <summary>
		/// Toggles the state of the bit parameter in the target using bitwise logic.
		/// </summary>
		/// <param name="target">The bits being changed.</param>
		/// <param name="bit">The bit to change.</param>
		/// <param name="state">If the bits should be turned on or off.</param>
		/// <returns></returns>
		public static int SetBit(this int target, int bit, bool state)
		{
			// only one bit can be changed at a time
			// therefore, the paramter must be a bit
			if (bit.IsBit() == false)
				throw new ArgumentException("Bit must be a bit value when calling ReadBit.", "bit");

			// since we need the bit state twice, i'm caching it :)
			bool BitState = target.ReadBit(bit);

			// if the bit is in the correct state, we're done
			if (BitState == state)
				return target;

			if (BitState == false && state == true)
				return target + bit;
			else
				return target - bit;
		}

		/// <summary>
		/// This method will take an integer value and determine if it is a bit value.  E.g. 1, 2, 4, 8, 16, etc
		/// </summary>
		/// <param name="target">The integer to test.</param>
		/// <returns></returns>
		public static bool IsBit(this int target)
		{
			// one is a special case, let's see if we got one
			if (target == 1)
				return true;
			else if (Math.Round(Math.Log(target)/Math.Log(2), 1) == Math.Log(target)/Math.Log(2))
				return true;
			else
				return false;
		}


		/// <summary>
		/// Tests the target to see if the bit is in the on position.
		/// </summary>
		/// <param name="target">The integer to test.</param>
		/// <param name="bit">The bit to test for.</param>
		/// <exception cref="ArgumentException">The bit parameter must be a valid bit.  E.g. 1, 2, 4, 8, 16, etc</exception>
		/// <returns></returns>
		public static bool ReadBit(this int target, int bit)
		{
			if (bit.IsBit() == false)
				throw new ArgumentException("Bit must be a bit value when calling ReadBit.", "bit");

			return (target & bit) == bit;
		}

		/// <summary>
		/// Tests to see if all of the bits are on in the target
		/// </summary>
		/// <param name="target">The integer to test.</param>
		/// <param name="bits">The bits to test for.</param>
		/// <returns></returns>
		public static bool IsBitUnion(this int target, int bits)
		{
			return (target & bits) == bits;
		}
	}
}
